datum/air_group
	var/group_processing = 1 //Processing all tiles as one large tile if 1

	var/datum/gas_mixture/air = new

	var/current_cycle = 0 //cycle that oxygen value represents
	var/archived_cycle = 0 //cycle that oxygen_archived value represents
		//The use of archived cycle saves processing power by permitting the archiving step of FET
		//	to be rolled into the updating step

	//optimization vars
	var/next_check = 0  //number of ticks before this group updates
	var/check_delay = 10  //number of ticks between updates, starts fairly high to get boring groups out of the way

	proc/members()
		//Returns the members of the group
	proc/process_group()


	var/list/borders //Tiles that connect this group to other groups/individual tiles
	var/list/members //All tiles in this group

	var/list/space_borders
	var/length_space_border = 0


	proc/suspend_group_processing()
		group_processing = 0
		update_tiles_from_group()
		check_delay=0
		next_check=0


	//Copy group air information to individual tile air
	//Used right before turning on group processing
	proc/update_group_from_tiles()
		var/sample_member = pick(members)
		var/datum/gas_mixture/sample_air = sample_member:air

		air.copy_from(sample_air)
		air.group_multiplier = members.len
		return 1


	//Copy group air information to individual tile air
	//Used right before turning off group processing
	proc/update_tiles_from_group()
		for(var/member in members)
			member:air.copy_from(air)
			if (istype(member,/turf/simulated))
				var/turf/simulated/turfmem=member
				turfmem.reset_delay()


	proc/archive()
		air.archive()
		archived_cycle = air_master.current_cycle


	//If individually processing tiles, checks all member tiles to see if they are close enough that the group may resume group processing
	//Warning: Do not call, called by air_master.process()
	proc/check_regroup()
		//Purpose: Checks to see if group processing should be turned back on
		//Returns: group_processing
		if(prevent_airgroup_regroup)
			return 0

		if(group_processing) return 1

		var/turf/simulated/sample = pick(members)
		for(var/member in members)
			if(member:active_hotspot)
				return 0
			if(member:air.compare(sample.air)) continue
			else
				return 0

		update_group_from_tiles()
		group_processing = 1
		return 1


//Look into this
	turf/process_group()
		current_cycle = air_master.current_cycle
		if(!group_processing)	 //Revert to individual processing then end
			for(var/T in members)
				var/turf/simulated/member = T
				member.process_cell()
			return

			//check if we're skipping this tick
		if (next_check > 0)
			next_check--
			return 1
		var/player_count = max(player_list.len, 3) / 3
		next_check += check_delay + rand(player_count, player_count * 1.5)
		check_delay++

		var/turf/simulated/list/border_individual = list()
		var/datum/air_group/list/border_group = list()

		var/turf/simulated/list/enemies = list() //used to send the appropriate border tile of a group to the group proc
		var/turf/simulated/list/self_group_borders = list()
		var/turf/simulated/list/self_tile_borders = list()

		if(archived_cycle < air_master.current_cycle)
			archive()
				//Archive air data for use in calculations
				//But only if another group didn't store it for us

		for(var/turf/simulated/border_tile in src.borders)
			for(var/direction in cardinal) //Go through all border tiles and get bordering groups and individuals
				if(border_tile.group_border&direction)
					var/turf/simulated/enemy_tile = get_step(border_tile, direction) //Add found tile to appropriate category
					if(istype(enemy_tile) && enemy_tile.parent && enemy_tile.parent.group_processing)
						border_group += enemy_tile.parent
						enemies += enemy_tile
						self_group_borders += border_tile
					else
						border_individual += enemy_tile
						self_tile_borders += border_tile

		var/abort_group = 0

		// Process connections to adjacent groups
		var/border_index = 1
		for(var/datum/air_group/AG in border_group)
			if(AG.archived_cycle < archived_cycle) //archive other groups information if it has not been archived yet this cycle
				AG.archive()
			if(AG.current_cycle < current_cycle)
				//This if statement makes sure two groups only process their individual connections once!
				//Without it, each connection would be processed a second time as the second group is evaluated

				var/connection_difference = 0
				var/turf/simulated/floor/self_border = self_group_borders[border_index]
				var/turf/simulated/floor/enemy_border = enemies[border_index]

				var/result = air.check_gas_mixture(AG.air)
				if(result == 1)
					connection_difference = air.share(AG.air)
				else if(result == -1)
					AG.suspend_group_processing()
					connection_difference = air.share(enemy_border.air)
				else
					abort_group = 1
					break

				if(connection_difference)
					if(connection_difference > 0)
						self_border.consider_pressure_difference(connection_difference, get_dir(self_border,enemy_border))
					else
						var/turf/enemy_turf = enemy_border
						if(!isturf(enemy_turf))
							enemy_turf = enemy_border.loc
						enemy_turf.consider_pressure_difference(-connection_difference, get_dir(enemy_turf,self_border))

				border_index++

		// Process connections to adjacent tiles
		border_index = 1
		if(!abort_group)
			for(var/atom/enemy_tile in border_individual)
				var/connection_difference = 0
				var/turf/simulated/floor/self_border = self_tile_borders[border_index]

				if(istype(enemy_tile, /turf/simulated))
					if(enemy_tile:archived_cycle < archived_cycle) //archive tile information if not already done
						enemy_tile:archive()
					if(enemy_tile:current_cycle < current_cycle)
						if(air.check_gas_mixture(enemy_tile:air))
							connection_difference = air.share(enemy_tile:air)
						else
							abort_group = 1
							break
				else if(isturf(enemy_tile))
					if(air.check_turf(enemy_tile))
						connection_difference = air.mimic(enemy_tile)
					else
						abort_group = 1
						break

				if(connection_difference)
					if(connection_difference > 0)
						self_border.consider_pressure_difference(connection_difference, get_dir(self_border,enemy_tile))
					else
						var/turf/enemy_turf = enemy_tile
						if(!isturf(enemy_turf))
							enemy_turf = enemy_tile.loc
						enemy_turf.consider_pressure_difference(-connection_difference, get_dir(enemy_tile,enemy_turf))

		// Process connections to space
		border_index = 1
		if(!abort_group)
			if(length_space_border > 0)
				var/turf/space/sample = locate()
				var/connection_difference = 0

				if(air.check_turf(sample))
					connection_difference = air.mimic(sample, length_space_border)
				else
					abort_group = 1

				if(connection_difference)
					for(var/turf/simulated/self_border in space_borders)
						self_border.consider_pressure_difference_space(connection_difference)

		if(abort_group)
			suspend_group_processing()
		else
			if(air.check_tile_graphic())
				for(var/T in members)
					var/turf/simulated/member = T
					member.update_visuals(air)


		if(air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
			for(var/T in members)
				var/turf/simulated/member = T
				member.hotspot_expose(air.temperature, CELL_VOLUME)
				member.consider_superconductivity(starting=1)

		air.react()
		return



	object/process_group()
		current_cycle = air_master.current_cycle

		if(!group_processing)	return //See if processing this group as a group

		var/turf/simulated/list/border_individual = list()
		var/datum/air_group/list/border_group = list()

		var/turf/simulated/list/enemies = list() //used to send the appropriate border tile of a group to the group proc
		var/enemy_index = 1

		if(archived_cycle < air_master.current_cycle)
			archive()
				//Archive air data for use in calculations
				//But only if another group didn't store it for us

		enemy_index = 1
		var/abort_group = 0
		for(var/datum/air_group/AG in border_group)
			if(AG.archived_cycle < archived_cycle) //archive other groups information if it has not been archived yet this cycle
				AG.archive()
			if(AG.current_cycle < current_cycle)
				//This if statement makes sure two groups only process their individual connections once!
				//Without it, each connection would be processed a second time as the second group is evaluated

				var/result = air.check_gas_mixture(AG.air)
				if(result == 1)
					air.share(AG.air)
				else if(result == -1)
					AG.suspend_group_processing()
					var/turf/simulated/floor/enemy_border = enemies[enemy_index]
					air.share(enemy_border.air)
				else
					abort_group = 0
					break
				enemy_index++

		if(!abort_group)
			for(var/enemy_tile in border_individual)
				if(istype(enemy_tile, /turf/simulated))
					if(enemy_tile:archived_cycle < archived_cycle) //archive tile information if not already done
						enemy_tile:archive()
					if(enemy_tile:current_cycle < current_cycle)
						if(air.check_gas_mixture(enemy_tile:air))
							air.share(enemy_tile:air)
						else
							abort_group = 1
							break
				else
					if(air.check_turf(enemy_tile))
						air.mimic(enemy_tile)
					else
						abort_group = 1
						break

		if(abort_group)
			suspend_group_processing()

		return